/**
 * 
 */
package org.swing.utility.jai.util;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import javax.media.jai.InterpolationBilinear;
import javax.media.jai.InterpolationNearest;
import javax.media.jai.JAI;
import javax.media.jai.operator.TransposeType;

import org.swing.utility.jai.bean.ImageSize;
import org.w3c.dom.Node;

import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifDirectory;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.iptc.IptcDirectory;
import com.drew.metadata.iptc.IptcReader;
/**
 * @author Quynh Nhu
 *
 */
public final class JaiUtils {
	  private static final int[] BYTES_PER_FORMAT = {
	    0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8
	  };

	  public static final int EXIF = 0xE1;

	  public static final int IPTC = 0xED;

	  private static final int FMT_BYTE = 1;

	  private static final int FMT_STRING = 2;

	  private static final int FMT_USHORT = 3;

	  private static final int FMT_ULONG = 4;

	  private static final int FMT_URATIONAL = 5;

	  private static final int FMT_SBYTE = 6;

	  private static final int FMT_UNDEFINED = 7;

	  private static final int FMT_SSHORT = 8;

	  private static final int FMT_SLONG = 9;

	  private static final int FMT_SRATIONAL = 10;

	  private static final int FMT_SINGLE = 11;

	  private static final int FMT_DOUBLE = 12;

	  private JaiUtils() {
	  }

	  public static Node getNativeMetadataNode(ImageReader reader)
	    throws IOException {
	    IIOMetadata metadata = reader.getImageMetadata(0);
	    if (metadata == null) {
	      return null;
	    } else {
	      return metadata.getAsTree(metadata.getNativeMetadataFormatName());
	    }
	  }

	  public static ExifDirectory getExifMetadataDirectory(ImageReader reader)
	    throws IOException {
	    return getExifMetadataDirectory(getNativeMetadataNode(reader));
	  }

	  public static ExifDirectory getExifMetadataDirectory(Node node) {
	    byte[] data = getDirectoryData(node, EXIF);
	    if (data != null) {
	      return (ExifDirectory)new ExifReader(data).extract().getDirectory(
	        ExifDirectory.class);
	    } else {
	      return null;
	    }
	  }

	  public static byte[] getDirectoryData(Node node, int value) {
	    if (node.getNodeName() == "unknown") {
	      if (Integer.parseInt(node.getAttributes()
	        .getNamedItem("MarkerTag")
	        .getNodeValue()) == EXIF) {
	        byte[] data = (byte[])((IIOMetadataNode)node).getUserObject();
	        return data;
	      }
	    }
	    Node child = node.getFirstChild();
	    while (child != null) {
	      byte[] data = getDirectoryData(child, value);
	      if (data != null) {
	        return data;
	      } else {
	        child = child.getNextSibling();
	      }
	    }
	    return null;
	  }

	  public static void setExifImageDimensions(byte[] data, int width, int height) {
	    ByteBuffer buffer = ByteBuffer.wrap(data);
	    buffer.position(6);
	    byte[] byteOrderData = new byte[2];
	    buffer.get(byteOrderData);
	    String byteOrderString = new String(byteOrderData);
	    // set the byte order II (intel) == Little, MM (motorola) == big)
	    if (byteOrderString.equals("II")) {
	      buffer.order(ByteOrder.LITTLE_ENDIAN);
	    } else {
	      buffer.order(ByteOrder.BIG_ENDIAN);
	    }
	    System.out.println(buffer.getShort());
	    for (int dirOffset = buffer.getInt(); dirOffset != 0; dirOffset = buffer.getInt()) {
	      buffer.position(dirOffset + 6);
	      processDirectory(buffer, width, height);
	    }
	  }

	  private static void processDirectory(ByteBuffer buffer, int width, int height) {
	    int numTags = buffer.getShort();
	    for (int i = 0; i < numTags; i++) {
	      int position = buffer.position();
	      int tagType = buffer.getShort();
	      if (tagType < 0) {
	        tagType = Short.MAX_VALUE - tagType;
	      }
	      System.out.println(tagType);
	      int formatCode = buffer.getShort();
	      int componentCount = buffer.getInt();
	      for (int j = 0; j < componentCount; j++) {
	        switch (formatCode) {
	          case FMT_BYTE:
	            byte value = buffer.get();
	          break;
	          case FMT_DOUBLE:
	            buffer.getDouble();
	          break;
	          case FMT_SBYTE:

	          break;
	          case FMT_SINGLE:

	          break;
	          case FMT_SLONG:
	            buffer.getInt();
	          break;
	          case FMT_SRATIONAL:
	            buffer.getInt();
	            buffer.getInt();
	          break;
	          case FMT_SSHORT:
	            buffer.getShort();
	          break;
	          case FMT_UNDEFINED:
	            buffer.get();
	          break;
	          case FMT_STRING:
	            buffer.get();
	          break;
	          case FMT_ULONG:
	            if (tagType == ExifDirectory.TAG_EXIF_IMAGE_WIDTH) {
	              buffer.putInt(width);
	            } else if (tagType == ExifDirectory.TAG_EXIF_IMAGE_HEIGHT) {
	              buffer.putInt(height);
	            } else {
	              short us = buffer.getShort();
	            }
	          break;
	          case FMT_URATIONAL:
	            buffer.getInt();
	            buffer.getInt();
	          break;
	          case FMT_USHORT:
	            if (tagType == ExifDirectory.TAG_EXIF_IMAGE_WIDTH) {
	              buffer.putShort((short)width);
	            } else if (tagType == ExifDirectory.TAG_EXIF_IMAGE_HEIGHT) {
	              buffer.putShort((short)height);
	            } else {
	              short us = buffer.getShort();
	            }
	          break;
	          default:
	          break;
	        }

	      }
	      buffer.position(position + 12);
	    }
	  }

	  public static byte[] getExifThumbnail(ImageReader reader)
	    throws MetadataException, IOException {
	    return getExifThumbnail(getExifMetadataDirectory(reader));
	  }

	  public static byte[] getExifThumbnail(Node node) throws MetadataException {
	    return getExifThumbnail(getExifMetadataDirectory(node));
	  }

	  public static byte[] getExifThumbnail(ExifDirectory exif)
	    throws MetadataException {
	    if (exif != null && exif.containsThumbnail()) {
	      return exif.getThumbnailData();
	    } else {
	      return null;
	    }
	  }

	  public static IptcDirectory getIptcMetadataDirectory(ImageReader reader)
	    throws IOException {
	    return getIptcMetadataDirectory(getNativeMetadataNode(reader));
	  }

	  public static IptcDirectory getIptcMetadataDirectory(Node node) {
	    if (node.getNodeName() == "unknown") {
	      if (Integer.parseInt(node.getAttributes()
	        .getNamedItem("MarkerTag")
	        .getNodeValue()) == IPTC) {
	        byte[] data = (byte[])((IIOMetadataNode)node).getUserObject();
	        return (IptcDirectory)new IptcReader(data).extract().getDirectory(
	          IptcDirectory.class);
	      }
	    }
	    Node child = node.getFirstChild();
	    while (child != null) {
	      IptcDirectory directory = getIptcMetadataDirectory(child);
	      if (directory != null) {
	        return directory;
	      }
	      child = child.getNextSibling();
	    }
	    return null;
	  }

	  public static RenderedImage transpose(final RenderedImage image,
	    TransposeType type) {
	    return (RenderedImage)JAI.create("transpose", image, type);
	  }

	  public static RenderedImage rotate(final RenderedImage image, int angle) {
	    ParameterBlock pb = new ParameterBlock();
	    pb.addSource(image); // The source image
	    pb.add(0.0F); // The x origin
	    pb.add(0.0F); // The y origin
	    pb.add((float)Math.toRadians(angle)); // The rotation angle
	    pb.add(new InterpolationNearest()); // The interpolation

	    return JAI.create("Rotate", pb, null);

	  }

	  public static RenderedImage scale(final RenderedImage image, final float scale) {
	    ParameterBlock pb = new ParameterBlock();
	    pb.addSource(image); // The source image
	    pb.add(scale); // The xScale
	    pb.add(scale); // The yScale
	    pb.add(0.0F); // The x translation
	    pb.add(0.0F); // The y translation
	    pb.add(new InterpolationBilinear()); // The interpolation
	    return JAI.create("scale", pb);

	  }

	  public static RenderedImage scaleToSize(final RenderedImage image,
	    final int maxWidth, final int maxHeight) {
	    float scaleX = (float)maxWidth / (float)image.getWidth();
	    float scaleY = (float)maxHeight / (float)image.getHeight();
	    float scale = Math.min(scaleX, scaleY);
	    return scale(image, scale);
	  }

	  public static RenderedImage scaleToSize(final RenderedImage image,
	    final ImageSize size) {
	    return scaleToSize(image, size.getWidth(), size.getHeight());
	  }

	  public static RenderedImage loadImage(final File file) {
	    return (RenderedImage)JAI.create("fileload", file.getAbsolutePath());
	  }
	  
	  public BufferedImage getJPEG2000Image(byte[] data){
		  
			ImageInputStream iis = null;
			BufferedImage image=null;
			try {
				iis = ImageIO.createImageInputStream(new ByteArrayInputStream(data));
				com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReaderSpi j2kImageReaderSpi = new com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReaderSpi();
				com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReader j2kReader = new com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReader(j2kImageReaderSpi);
				j2kReader.setInput(iis, true);
				image = j2kReader.read(0, new com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam());
			}
			catch (Exception e){
				e.printStackTrace();
			}
			return image;
		}
	}
